<?php

namespace App\Services;

use App\BaseException\BaseException;

// 1. 资源及配置加载
# 加载 SDK 初始化文件
class BaofuWithdrawPayService
{
    protected $Type;
    protected $Member_Id;
    protected $Terminal_Id;
    protected $PFX_Path;
    protected $PRI_PASS;
    protected $CER_Path;
    protected $POST_URL;
    private $trans_reqDatas = array();

    public $values = array('trans_no' => '商户订单号',
        'trans_money' => '转账金额',
        'to_acc_name' => '收款人姓名',
        'to_acc_no' => '收款人银行帐号',
        'to_bank_name' => '收款人银行名称',
        'to_pro_name' => '',//收款人开户行省名
        'to_city_name' => '',//收款人开户行市名
        'to_acc_dept' => '',//收款人开户行机构名
        'trans_card_id' => '',//银行卡身份证件号码
        'trans_mobile' => '',//银行卡预留手机号
        'trans_summary' => '摘要',
        'trans_reserved' => '');//用途

    public function __construct()
    {
        $Path = $_SERVER['DOCUMENT_ROOT'];
        $Path .= "/CER/";
        $this->Type = "json";
        $this->Member_Id = "100000178";    //商户号
        $this->Terminal_Id = "100000859";    //终端号
        $this->PFX_Path = $Path . "m_pri.pfx";
        $this->PRI_PASS = "123456";
        $this->CER_Path = $Path . "baofoo_pub.cer";
    }

    /**
     * 记录日志
     * @param $message
     * @param array $request
     * @param array $response
     */
    public static function requestLog($message, $request = [], $response = [])
    {
        $content = [
            'request' => is_object($request) ? object_array($request) : $request,
            'response' => is_object($response) ? object_array($response) : $response,
        ];
        \Illuminate\Support\Facades\Log::debug($message, $content);
    }

    /**
     * 代付交易接口
     * @param $params
     * @return void
     */
    public function pay($params = [])
    {
        $datetype = "json";
        $test = new self();
        $test->_set("trans_no", "TN" . self::getTransid() . self::getRand4());
        $test->_set("trans_money", "1");
        $test->_set("to_acc_name", "王勇");
        $test->_set("to_acc_no", "6212262201023557228");
        $test->_set("to_bank_name", "中国工商银行");
        $test->_set("to_pro_name", "上海市");
        $test->_set("to_city_name", "上海市");
        $test->_set("to_acc_dept", "支行");
        $test->_set("trans_card_id", "310115200501018559");

// 添加到trans_reqDatas
        if ($datetype == "json") {
            self::__array_json_push($test->_getValues());
        } else {
            self::__array_push($test->_getValues());
        }


        $transReqDatas = self::__getTransReqDatas();
        if ($datetype == "json") {
            $tmp = array();
            array_push($tmp, array("trans_reqData" => $transReqDatas));
            $trans_content=[];
            $trans_content['trans_reqDatas']=$tmp;
            $trans_content_array=[];
            $trans_content_array['trans_content']=$trans_content;
            $data_content = self:: __array2Json($trans_content_array);
            $data_content = str_replace("\\\"", '"', $data_content);
        } else {
            $trans_content=[];
            $trans_content['trans_reqDatas']=$transReqDatas;
            $data_content = self:: __array2Xml($trans_content);
        }

       var_dump("报文体：" . $data_content);

        $Request_url = "https://paytest.baofoo.com/baofoo-fopay/pay/BF0040001.do";
// 私钥加密
        $encrypted = self::encryptByPFXFile($data_content, $this->PFX_Path,$this->PRI_PASS);

        $PostArry = array();
        $PostArry["member_id"] = $this->Member_Id;
        $PostArry["terminal_id"] =$this->Terminal_Id;
        $PostArry["data_type"] = $this->Type;
        $PostArry["version"] = "4.0.0";
        $PostArry["data_content"] = $encrypted;


        $httpResult = self::Post($PostArry, $Request_url);
        if (count(explode("trans_content", $httpResult)) > 1) {
            //基本信息处理（明文返回）
            echo '宝付明文同步返回:' . $httpResult . '<br>';
        } else {
            //业务逻辑信息处理
            $decrypt = self::decryptByCERFile($httpResult, $this->CER_Path);
            echo '宝付加密同步返回:' . $decrypt . '<br>';
        }

    }

    /**
     * 代付交易状态查证接口
     * @param $params
     * @return void
     */
    public function queryOrder($params = [])
    {
        $datetype = "json";
        $test = new self();
        $test->_set("trans_batchid", "");
        $test->_set("trans_no", "TN16786769038155");


        if ($datetype == "json") {
            self::__array_json_push($test->_getValues());
        } else {
            self::__array_push($test->_getValues());
        }

        $transReqDatas = self::__getTransReqDatas();
        if ($datetype == "json") {
            $tmp = array();
            array_push($tmp, array("trans_reqData" => $transReqDatas));
            $trans_content=[];
            $trans_content['trans_reqDatas']=$tmp;
            $trans_content_array=[];
            $trans_content_array['trans_content']=$trans_content;
            $data_content = self:: __array2Json($trans_content_array);
            $data_content = str_replace("\\\"", '"', $data_content);
        } else {
            $trans_content=[];
            $trans_content['trans_reqDatas']=$transReqDatas;
            $data_content = self:: __array2Xml($trans_content);
        }

        var_dump("报文体：" . $data_content);

        $Request_url = "https://paytest.baofoo.com/baofoo-fopay/pay/BF0040002.do";
// 私钥加密
        $encrypted = self::encryptByPFXFile($data_content, $this->PFX_Path,$this->PRI_PASS);

        $PostArry = array();
        $PostArry["member_id"] = $this->Member_Id;
        $PostArry["terminal_id"] =$this->Terminal_Id;
        $PostArry["data_type"] = $this->Type;
        $PostArry["version"] = "4.0.0";
        $PostArry["data_content"] = $encrypted;


        $httpResult = self::Post($PostArry, $Request_url);
        if (count(explode("trans_content", $httpResult)) > 1) {
            //基本信息处理（明文返回）
            echo '宝付明文同步返回:' . $httpResult . '<br>';
        } else {
            //业务逻辑信息处理
            $decrypt = self::decryptByCERFile($httpResult, $this->CER_Path);
            echo '宝付加密同步返回:' . $decrypt . '<br>';
        }

    }
    /**
     * 代付交易退款查证接口
     * @param $params
     * @return void
     */
    public function queryRefundOrder($params = [])
    {
        $datetype = "json";
        $test = new self();
        $test->_set("trans_btime", "20230312");
        $test->_set("trans_etime", "20230312");


        if ($datetype == "json") {
            self::__array_json_push($test->_getValues());
        } else {
            self::__array_push($test->_getValues());
        }

        $transReqDatas = self::__getTransReqDatas();
        if ($datetype == "json") {
            $tmp = array();
            array_push($tmp, array("trans_reqData" => $transReqDatas));
            $trans_content=[];
            $trans_content['trans_reqDatas']=$tmp;
            $trans_content_array=[];
            $trans_content_array['trans_content']=$trans_content;
            $data_content = self:: __array2Json($trans_content_array);
            $data_content = str_replace("\\\"", '"', $data_content);
        } else {
            $trans_content=[];
            $trans_content['trans_reqDatas']=$transReqDatas;
            $data_content = self:: __array2Xml($trans_content);
        }

        var_dump("报文体：" . $data_content);

        $Request_url = "https://paytest.baofoo.com/baofoo-fopay/pay/BF0040003.do";
// 私钥加密
        $encrypted = self::encryptByPFXFile($data_content, $this->PFX_Path,$this->PRI_PASS);

        $PostArry = array();
        $PostArry["member_id"] = $this->Member_Id;
        $PostArry["terminal_id"] =$this->Terminal_Id;
        $PostArry["data_type"] = $this->Type;
        $PostArry["version"] = "4.0.0";
        $PostArry["data_content"] = $encrypted;


        $httpResult = self::Post($PostArry, $Request_url);
        if (count(explode("trans_content", $httpResult)) > 1) {
            //基本信息处理（明文返回）
            echo '宝付明文同步返回:' . $httpResult . '<br>';
        } else {
            //业务逻辑信息处理
            $decrypt = self::decryptByCERFile($httpResult, $this->CER_Path);
            echo '宝付加密同步返回:' . $decrypt . '<br>';
        }

    }


    // __get()方法用来获取私有属性
    public function _get($property_name)
    {
        if (isset($this->values[$property_name])) {  //判断一下
            return $this->values[$property_name];
        } else {
            echo '没有此属性！[' . $property_name . ']<br>';
        }
    }

    // __set()方法用来设置私有属性
    public function _set($property_name, $value)
    {
        if (isset($this->values[$property_name])) {  //这里也判断一下
            $this->values[$property_name] = $this->validate($value);
        } else {
            echo '没有此属性！[' . $property_name . ']<br>';
        }
    }

    //_getValues获取方法
    public function _getValues()
    {
        return $this->values;
    }

    private function validate($value)
    {
        return htmlspecialchars(addslashes($value));
    }


    /**
     * 生成时间戳
     * @return type
     */
    public static function getTransid()
    {
        return strtotime(date('Y-m-d H:i:s', time()));
    }

    /**
     * 生成四位随机数
     * @return type
     */
    public static function getRand4()
    {
        return rand(1000, 9999);
    }

    /**
     * 生成四位随机数
     * @return type
     */
    public static function getTime()
    {
        return date('Y-m-d H:i:s', time());
    }

    /**
     * 排序输出k=v&k1=v1.....格式
     * @param type $DArray
     * @return type
     */
    public static function SortAndOutString($DArray)
    {
        $TempData = array();
        foreach ($DArray as $Key => $Value) {
            if (!self::isBlank($Value)) {
                $TempData[$Key] = $Value;
            }
        }
        ksort($TempData);//排序
        return http_build_query($TempData);
    }

    /**
     * 判断是否空值
     * @param type $Strings
     * @return boolean
     */
    public static function isBlank($Strings)
    {
        $Strings = trim($Strings);
        if ((empty($Strings) || ($Strings == null)) && (strlen($Strings) <= 0)) {
            return true;
        } else {
            return FALSE;
        }
    }

    /**
     * 删除数组元素
     * @param type $data
     * @param type $key
     * @return type
     */
    public static function array_remove($data, $key)
    {
        if (!array_key_exists($key, $data)) {
            return $data;
        }
        $keys = array_keys($data);
        $index = array_search($key, $keys);
        if ($index !== FALSE) {
            array_splice($data, $index, 1);
        }
        return $data;
    }

    /**
     * 获取信封中的key值
     * @param type $Strings
     * @return type
     * @throws BaseException
     */
    public static function getAesKey($Strings)
    {
        $KeyArray = explode("|", $Strings);
        if (count($KeyArray) == 2) {
            $KeyArray[1] = trim($KeyArray[1]);
            if (!empty($KeyArray[1])) {
                return $KeyArray[1];
            } else {
                throw new BaseException("Key is Null!");
            }
        } else {
            throw new BaseException("Data format is incorrect!");
        }
    }

    public static function __array2Xml($trans_content)
    {
        $doc = new DOMDocument('1.0', 'UTF-8');
        $doc->formatOutput = true;
        $root = $doc->createElement('trans_content');
        $root = $doc->appendChild($root);
        foreach ($trans_content as $content_child => $content_child_value) {
            //trans_content子节点
            $content_child = $doc->createElement($content_child);
            $content_child = $root->appendChild($content_child);
            if (is_array($content_child_value)) {
                //创建trans_content子节点
                foreach ($content_child_value as $child_key => $child_value) {
                    //trans_reqDatas
                    if (is_array($child_value)) {
                        foreach ($child_value as $trans_reqDatas => $trans_reqData) {
                            //创建trans_reqDatas子节点trans_reqData
                            $trans_reqDatas = $doc->createElement($trans_reqDatas);
                            $trans_reqDatas = $content_child->appendChild($trans_reqDatas);
                            //遍历trans_reqData子节点并创建属性和值
                            foreach ($trans_reqData as $reqData_key => $reqData_value) {
                                $reqData_key = $doc->createElement($reqData_key);
                                $reqData_key = $trans_reqDatas->appendChild($reqData_key);
                                $text = $doc->createTextNode($reqData_value);
                                $text = $reqData_key->appendChild($text);
                            }
                        }
                    } else {
                        $child_key = $doc->createElement($child_key);
                        $child_key = $content_child->appendChild($child_key);
                        $text = $doc->createTextNode($child_value);
                        $text = $child_key->appendChild($text);
                    }
                }
            } else {
                $text = $doc->createTextNode($content_child_value);
                $text = $title->appendChild($text);
            }
        }
        return $doc->saveXML();
    }

    //
    public function __array2Json($array)
    {
        return json_encode($array, JSON_UNESCAPED_UNICODE);
    }

    /**
     * 读取私钥
     * @param type $PfxPath
     * @param type $PrivateKPASS
     * @return array
     * @throws BaseException
     */
    private static function getPriveKey($PfxPath, $PrivateKPASS)
    {
        try {
            if (!file_exists($PfxPath)) {
                throw new BaseException("私钥文件不存在！路径：" . $PfxPath);
            }
            $PKCS12 = file_get_contents($PfxPath);
            $PrivateKey = array();
            if (openssl_pkcs12_read($PKCS12, $PrivateKey, $PrivateKPASS)) {
                return $PrivateKey["pkey"];
            } else {
                throw new BaseException("私钥证书读取出错！原因[证书或密码不匹配]，请检查本地证书相关信息。");
            }
        } catch (BaseException $ex) {
            $ex->getTrace();
        }
    }

    /**
     * 读取公钥
     * @param type $PublicPath
     * @return type
     * @throws BaseException
     */
    private static function getPublicKey($PublicPath)
    {
        try {
            if (!file_exists($PublicPath)) {
                throw new BaseException("公钥文件不存在！路径：" . $PublicPath);
            }
            $KeyFile = file_get_contents($PublicPath);
            $PublicKey = openssl_get_publickey($KeyFile);
            if (empty($PublicKey)) {
                throw new BaseException("公钥不可用！路径：" . $PublicPath);
            }
            return $PublicKey;
        } catch (BaseException $ex) {
            $ex->getTraceAsString();
        }
    }


    /**
     * 私钥加密
     * @param type $Data
     * @param type $PfxPath
     * @param type $PrivateKPASS
     * @return type
     * @throws BaseException
     */
    public static function encryptByPFXFile($Data, $PfxPath, $PrivateKPASS)
    {
        try {
            if (!function_exists('bin2hex')) {
                throw new BaseException("bin2hex PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPriveKey($PfxPath, $PrivateKPASS);
            $BASE64EN_DATA = base64_encode($Data);
            $EncryptStr = "";
            $blockSize = self::get_Key_Size($KeyObj);
            if ($blockSize <= 0) {
                throw new BaseException("BlockSize is 0");
            } else {
                $blockSize = $blockSize / 8 - 11;//分段
            }
            $totalLen = strlen($BASE64EN_DATA);
            $EncryptSubStarLen = 0;
            $EncryptTempData = "";
            while ($EncryptSubStarLen < $totalLen) {
                openssl_private_encrypt(substr($BASE64EN_DATA, $EncryptSubStarLen, $blockSize), $EncryptTempData, $KeyObj);
                $EncryptStr .= bin2hex($EncryptTempData);
                $EncryptSubStarLen += $blockSize;
            }
            return $EncryptStr;
        } catch (BaseException $exc) {
            echo $exc->getTraceAsString();
        }
    }

    /**
     * 私钥解密
     * @param type $Data 解密数据
     * @param type $PublicPath 解密公钥路径
     * @return type
     * @throws BaseException
     */
    public static function decryptByPFXFile($Data, $PfxPath, $PrivateKPASS)
    {
        try {
            if (!function_exists('hex2bin')) {
                throw new BaseException("hex2bin PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPriveKey($PfxPath, $PrivateKPASS);
            $blockSize = self::get_Key_Size($KeyObj);
            if ($blockSize <= 0) {
                throw new BaseException("BlockSize is 0");
            } else {
                $blockSize = $blockSize / 4;
            }
            $DecryptRsult = "";
            $totalLen = strlen($Data);
            $EncryptSubStarLen = 0;
            $DecryptTempData = "";
            while ($EncryptSubStarLen < $totalLen) {
                openssl_private_decrypt(hex2bin(substr($Data, $EncryptSubStarLen, $blockSize)), $DecryptTempData, $KeyObj);
                $DecryptRsult .= $DecryptTempData;
                $EncryptSubStarLen += $blockSize;
            }
            return base64_decode($DecryptRsult);
        } catch (BaseException $exc) {
            echo $exc->getTraceAsString();
        }
    }

    /**
     * 公钥解密
     * @param type $Data
     * @param type $PublicPath
     * @return type
     * @throws BaseException
     */
    public static function decryptByCERFile($Data, $PublicPath)
    {
        try {
            if (!function_exists('hex2bin')) {
                throw new BaseException("hex2bin PHP5.4及以上版本支持此函数，也可自行实现！");
            }
            $KeyObj = self::getPublicKey($PublicPath);
            $DecryptRsult = "";
            $blockSize = self::get_Key_Size($KeyObj, false);
            if ($blockSize <= 0) {
                throw new BaseException("BlockSize is 0");
            } else {
                $blockSize = $blockSize / 4;
            }
            $totalLen = strlen($Data);
            $EncryptSubStarLen = 0;
            $DecryptTempData = "";
            while ($EncryptSubStarLen < $totalLen) {
                openssl_public_decrypt(hex2bin(substr($Data, $EncryptSubStarLen, $blockSize)), $DecryptTempData, $KeyObj);
                $DecryptRsult .= $DecryptTempData;
                $EncryptSubStarLen += $blockSize;
            }
            return base64_decode($DecryptRsult);
        } catch (BaseException $exc) {
            echo $exc->getTraceAsString();
        }
    }

    /**
     * 获取证书长度
     * @param type $Key_String
     * @param type $Key_Type
     * @return int
     * @throws BaseException
     */
    private static function get_Key_Size($Key_String, $Key_Type = true)
    {
        $Key_Temp = array();
        try {
            if ($Key_Type) {//私钥
                $Key_Temp = openssl_pkey_get_details(openssl_pkey_get_private($Key_String));
            } else if (openssl_pkey_get_public($Key_String)) {//公钥
                $Key_Temp = openssl_pkey_get_details(openssl_pkey_get_public($Key_String));
            } else {
                throw new BaseException("Is not a key");
            }
            if (array_key_exists("bits", $Key_Temp)) {
                return $Key_Temp["bits"];
            } else {
                return 0;
            }
        } catch (BaseException $ex) {
            $ex->getTrace();
            return 0;
        }

    }

    public static function Post($PostArry,$Req_Url){
        try {
            $postData = $PostArry;
            $postDataString = http_build_query($postData);//格式化参数
            var_dump("发送参数：".$Req_Url."?".$postDataString);
            $curl = curl_init(); // 启动一个CURL会话
            curl_setopt($curl, CURLOPT_URL, $Req_Url); // 要访问的地址
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
            //curl_setopt($curl, CURLOPT_SSLVERSION, 6); //CURL_SSLVERSION_DEFAULT (0), CURL_SSLVERSION_TLSv1 (1), CURL_SSLVERSION_SSLv2 (2), CURL_SSLVERSION_SSLv3 (3), CURL_SSLVERSION_TLSv1_0 (4), CURL_SSLVERSION_TLSv1_1 (5)， CURL_SSLVERSION_TLSv1_2 (6) 中的其中一个。
            curl_setopt($curl, CURLOPT_POST, true); //发送一个常规的Post请求
            curl_setopt($curl, CURLOPT_POSTFIELDS, $postDataString); // Post提交的数据包
            curl_setopt($curl, CURLOPT_TIMEOUT, 60); // 设置超时限制防止死循环返回
            curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
            $tmpInfo = curl_exec($curl); // 执行操作
            if (curl_errno($curl)) {
                $tmpInfo = curl_error($curl);//捕抓异常
            }
            curl_close($curl); // 关闭CURL会话
            return $tmpInfo; // 返回数据
        } catch (BaseException $ex) {
            echo $ex->getTraceAsString();
        }
    }

    // 保存值
    public function __array_push($array)//XML格式
    {
        //echo "TransReqDatas set:$name=$array","\n";
        $trans_reqData = array("trans_reqData"=>$array);

        array_push($this->trans_reqDatas,$trans_reqData);
    }

    public  function __array_json_push($array){ //JSON格式
        array_push($this->trans_reqDatas,$array);
    }

// 取得属性名称对应的值
    public  function __getArray2Json()
    {
        return json_encode($this->trans_reqDatas,JSON_UNESCAPED_UNICODE);
    }

    public  function __getTransReqDatas()
    {
        return $this->trans_reqDatas;
    }

}
